#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <sys/types.h>
#include <iostream>
#include <cstring>

#if HAVE_STROPTS_H
# include <stropts.h>
#endif
#include <sys/ioctl.h>
#include <libintl.h>
#include <unistd.h>
#include <errno.h>

/* We define these the same for all machines.
   Changes from this to the outside world should be done in `_exit'.  */
#define	EXIT_FAILURE	1	/* Failing exit status.  */
#define	EXIT_SUCCESS	0	/* Successful exit status.  */

#define _(msgid) gettext (msgid)
#define N_(msgid) msgid

/* The official name of this program (e.g., no 'g' prefix).  */
#define PROGRAM_NAME "cat"

#define HELP_OPTION_DESCRIPTION \
  _("      --help     display this help and exit\n")
#define VERSION_OPTION_DESCRIPTION \
  _("      --version  output version information and exit\n")

#define AUTHORS \
  proper_name_utf8 ("Torbjorn Granlund", "Torbj\303\266rn Granlund"), \
  proper_name ("Richard M. Stallman")

/* Name of input file.  May be "-".  */
static char const *infile;

/* Descriptor on which input file is open.  */
static int input_desc;

/* Buffer for line numbers.
   An 11 digit counter may overflow within an hour on a P2/466,
   an 18 digit counter needs about 1000y */
#define LINE_COUNTER_BUF_LEN 20
static char line_buf[LINE_COUNTER_BUF_LEN] =
  {
    ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
    ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0',
    '\t', '\0'
  };

/* Position in 'line_buf' where printing starts.  This will not change
   unless the number of lines is larger than 999999.  */
static char *line_num_print = line_buf + LINE_COUNTER_BUF_LEN - 8;

/* Position of the first digit in 'line_buf'.  */
static char *line_num_start = line_buf + LINE_COUNTER_BUF_LEN - 3;

/* Position of the last digit in 'line_buf'.  */
static char *line_num_end = line_buf + LINE_COUNTER_BUF_LEN - 3;

/* Preserves the 'cat' function's local 'newlines' between invocations.  */
static int newlines2 = 0;

void
usage (int status)
{
  if (status != EXIT_SUCCESS)
  	std::cout << "emit_try_help" << std::endl;
    //emit_try_help ();
  else
    {
      printf (_("\
Usage: %s [OPTION]... [FILE]...\n\
"),
              program_name);
      fputs (_("\
Concatenate FILE(s), or standard input, to standard output.\n\
\n\
  -A, --show-all           equivalent to -vET\n\
  -b, --number-nonblank    number nonempty output lines, overrides -n\n\
  -e                       equivalent to -vE\n\
  -E, --show-ends          display $ at end of each line\n\
  -n, --number             number all output lines\n\
  -s, --squeeze-blank      suppress repeated empty output lines\n\
"), stdout);
      fputs (_("\
  -t                       equivalent to -vT\n\
  -T, --show-tabs          display TAB characters as ^I\n\
  -u                       (ignored)\n\
  -v, --show-nonprinting   use ^ and M- notation, except for LFD and TAB\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\
\n\
With no FILE, or when FILE is -, read standard input.\n\
"), stdout);
      printf (_("\
\n\
Examples:\n\
  %s f - g  Output f's contents, then standard input, then g's contents.\n\
  %s        Copy standard input to standard output.\n\
"),
              program_name, program_name);
      std::cout << "emit_ancillary_info" << std::endl;
      //emit_ancillary_info ();
    }
  exit (status);
}

/* Compute the next line number.  */

static void
next_line_num (void)
{
  char *endp = line_num_end;
  do
    {
      if ((*endp)++ < '9')
        return;
      *endp-- = '0';
    }
  while (endp >= line_num_start);
  if (line_num_start > line_buf)
    *--line_num_start = '1';
  else
    *line_buf = '>';
  if (line_num_start < line_num_print)
    line_num_print--;
}

/* Plain cat.  Copies the file behind 'input_desc' to STDOUT_FILENO.
   Return true if successful.  */

static bool
simple_cat (
     /* Pointer to the buffer, used by reads and writes.  */
     char *buf,

     /* Number of characters preferably read or written by each read and write
        call.  */
     size_t bufsize)
{
  /* Actual number of characters read, and therefore written.  */
  size_t n_read;

  /* Loop until the end of the file.  */

  while (true)
    {
      /* Read a block of input.  */

      n_read = safe_read (input_desc, buf, bufsize);
      if (n_read == SAFE_READ_ERROR)
        {
          std::cout << "error" << std::endl;
          return false;
        }

      /* End of this file?  */

      if (n_read == 0)
        return true;

      /* Write this block out.  */

      {
        /* The following is ok, since we know that 0 < n_read.  */
        size_t n = n_read;
        if (full_write (STDOUT_FILENO, buf, n) != n)
          std::cout << "error" << std::endl;
      }
    }
}

/* Write any pending output to STDOUT_FILENO.
   Pending is defined to be the *BPOUT - OUTBUF bytes starting at OUTBUF.
   Then set *BPOUT to OUTPUT if it's not already that value.  */

static inline void
write_pending (char *outbuf, char **bpout)
{
  size_t n_write = *bpout - outbuf;
  if (0 < n_write)
    {
      if (full_write (STDOUT_FILENO, outbuf, n_write) != n_write)
        std::cout << "error" << std::endl;
      *bpout = outbuf;
    }
}

/* Cat the file behind INPUT_DESC to the file behind OUTPUT_DESC.
   Return true if successful.
   Called if any option more than -u was specified.

   A newline character is always put at the end of the buffer, to make
   an explicit test for buffer end unnecessary.  */

static bool
cat (
     /* Pointer to the beginning of the input buffer.  */
     char *inbuf,

     /* Number of characters read in each read call.  */
     size_t insize,

     /* Pointer to the beginning of the output buffer.  */
     char *outbuf,

     /* Number of characters written by each write call.  */
     size_t outsize,

     /* Variables that have values according to the specified options.  */
     bool show_nonprinting,
     bool show_tabs,
     bool number,
     bool number_nonblank,
     bool show_ends,
     bool squeeze_blank)
{
  /* Last character read from the input buffer.  */
  unsigned char ch;

  /* Pointer to the next character in the input buffer.  */
  char *bpin;

  /* Pointer to the first non-valid byte in the input buffer, i.e. the
     current end of the buffer.  */
  char *eob;

  /* Pointer to the position where the next character shall be written.  */
  char *bpout;

  /* Number of characters read by the last read call.  */
  size_t n_read;

  /* Determines how many consecutive newlines there have been in the
     input.  0 newlines makes NEWLINES -1, 1 newline makes NEWLINES 1,
     etc.  Initially 0 to indicate that we are at the beginning of a
     new line.  The "state" of the procedure is determined by
     NEWLINES.  */
  int newlines = newlines2;

#ifdef FIONREAD
  /* If nonzero, use the FIONREAD ioctl, as an optimization.
     (On Ultrix, it is not supported on NFS file systems.)  */
  bool use_fionread = true;
#endif

  /* The inbuf pointers are initialized so that BPIN > EOB, and thereby input
     is read immediately.  */

  eob = inbuf;
  bpin = eob + 1;

  bpout = outbuf;

  while (true)
    {
      do
        {
          /* Write if there are at least OUTSIZE bytes in OUTBUF.  */

          if (outbuf + outsize <= bpout)
            {
              char *wp = outbuf;
              size_t remaining_bytes;
              do
                {
                  if (full_write (STDOUT_FILENO, wp, outsize) != outsize)
                    std::cerr << "error" << std::endl;
                  wp += outsize;
                  remaining_bytes = bpout - wp;
                }
              while (outsize <= remaining_bytes);

              /* Move the remaining bytes to the beginning of the
                 buffer.  */

              memmove (outbuf, wp, remaining_bytes);
              bpout = outbuf + remaining_bytes;
            }

          /* Is INBUF empty?  */

          if (bpin > eob)
            {
              bool input_pending = false;
#ifdef FIONREAD
              int n_to_read = 0;

              /* Is there any input to read immediately?
                 If not, we are about to wait,
                 so write all buffered output before waiting.  */

              if (use_fionread
                  && ioctl (input_desc, FIONREAD, &n_to_read) < 0)
                {
                  /* Ultrix returns EOPNOTSUPP on NFS;
                     HP-UX returns ENOTTY on pipes.
                     SunOS returns EINVAL and
                     More/BSD returns ENODEV on special files
                     like /dev/null.
                     Irix-5 returns ENOSYS on pipes.  */
                  if (errno == EOPNOTSUPP || errno == ENOTTY
                      || errno == EINVAL || errno == ENODEV
                      || errno == ENOSYS)
                    use_fionread = false;
                  else
                    {
                      std::cerr << "error" << std::endl;
                      newlines2 = newlines;
                      return false;
                    }
                }
              if (n_to_read != 0)
                input_pending = true;
#endif

              if (!input_pending)
                write_pending (outbuf, &bpout);

              /* Read more input into INBUF.  */

              n_read = safe_read (input_desc, inbuf, insize);
              if (n_read == SAFE_READ_ERROR)
                {
                  std::cerr << "error" << std::endl;
                  write_pending (outbuf, &bpout);
                  newlines2 = newlines;
                  return false;
                }
              if (n_read == 0)
                {
                  write_pending (outbuf, &bpout);
                  newlines2 = newlines;
                  return true;
                }

              /* Update the pointers and insert a sentinel at the buffer
                 end.  */

              bpin = inbuf;
              eob = bpin + n_read;
              *eob = '\n';
            }
          else
            {
              /* It was a real (not a sentinel) newline.  */

              /* Was the last line empty?
                 (i.e. have two or more consecutive newlines been read?)  */

              if (++newlines > 0)
                {
                  if (newlines >= 2)
                    {
                      /* Limit this to 2 here.  Otherwise, with lots of
                         consecutive newlines, the counter could wrap
                         around at INT_MAX.  */
                      newlines = 2;

                      /* Are multiple adjacent empty lines to be substituted
                         by single ditto (-s), and this was the second empty
                         line?  */
                      if (squeeze_blank)
                        {
                          ch = *bpin++;
                          continue;
                        }
                    }

                  /* Are line numbers to be written at empty lines (-n)?  */

                  if (number && !number_nonblank)
                    {
                      next_line_num ();
                      bpout = stpcpy (bpout, line_num_print);
                    }
                }

              /* Output a currency symbol if requested (-e).  */

              if (show_ends)
                *bpout++ = '$';

              /* Output the newline.  */

              *bpout++ = '\n';
            }
          ch = *bpin++;
        }
      while (ch == '\n');

      /* Are we at the beginning of a line, and line numbers are requested?  */

      if (newlines >= 0 && number)
        {
          next_line_num ();
          bpout = stpcpy (bpout, line_num_print);
        }

      /* Here CH cannot contain a newline character.  */

      /* The loops below continue until a newline character is found,
         which means that the buffer is empty or that a proper newline
         has been found.  */

      /* If quoting, i.e. at least one of -v, -e, or -t specified,
         scan for chars that need conversion.  */
      if (show_nonprinting)
        {
          while (true)
            {
              if (ch >= 32)
                {
                  if (ch < 127)
                    *bpout++ = ch;
                  else if (ch == 127)
                    {
                      *bpout++ = '^';
                      *bpout++ = '?';
                    }
                  else
                    {
                      *bpout++ = 'M';
                      *bpout++ = '-';
                      if (ch >= 128 + 32)
                        {
                          if (ch < 128 + 127)
                            *bpout++ = ch - 128;
                          else
                            {
                              *bpout++ = '^';
                              *bpout++ = '?';
                            }
                        }
                      else
                        {
                          *bpout++ = '^';
                          *bpout++ = ch - 128 + 64;
                        }
                    }
                }
              else if (ch == '\t' && !show_tabs)
                *bpout++ = '\t';
              else if (ch == '\n')
                {
                  newlines = -1;
                  break;
                }
              else
                {
                  *bpout++ = '^';
                  *bpout++ = ch + 64;
                }

              ch = *bpin++;
            }
        }
      else
        {
          /* Not quoting, neither of -v, -e, or -t specified.  */
          while (true)
            {
              if (ch == '\t' && show_tabs)
                {
                  *bpout++ = '^';
                  *bpout++ = ch + 64;
                }
              else if (ch != '\n')
                *bpout++ = ch;
              else
                {
                  newlines = -1;
                  break;
                }

              ch = *bpin++;
            }
        }
    }
}

void displayFileContent(const std::string& filename, bool showLineNumbers, bool squeezeEmptyLines) {
    std::ifstream file(filename);
    if (!file.is_open()) {
        std::cerr << "Error: Unable to open file " << filename << std::endl;
        return;
    }

    std::string line;
    int lineNumber = 1;
    bool prevLineEmpty = false;

    while (std::getline(file, line)) {
        if (squeezeEmptyLines && line.empty() && prevLineEmpty) {
            continue;
        }

        if (showLineNumbers) {
            std::cout << lineNumber << " ";
        }

        std::cout << line << std::endl;

        if (!line.empty()) {
            prevLineEmpty = false;
            ++lineNumber;
        } else {
            prevLineEmpty = true;
        }
    }

    file.close();
}

int main(int argc, char* argv[]) {

	  /* 指向输入缓冲区的指针。 */
	  char *inbuf;

	  /* 指向输出缓冲区的指针。 */
	  char *outbuf;

	  bool ok = true;
	  int c;

	  /* 要处理的参数在argv中的索引。 */
	  int argind;

	  /* 如果输出文件不应与任何输入文件相同，则为true。 */
	  bool check_redirection = true;

	  /* 如果我们曾经读取过标准输入，则为非零值。 */
	  bool have_read_stdin = false;

	  struct stat stat_buf;

	  /* 根据指定的选项设置的变量。 */
	  bool number = false;
	  bool number_nonblank = false;
	  bool squeeze_blank = false;
	  bool show_ends = false;
	  bool show_nonprinting = false;
	  bool show_tabs = false;
	  int file_open_mode = O_RDONLY;


	  static struct option const long_options[] =
	  {
		{"number-nonblank", no_argument, NULL, 'b'},
		{"number", no_argument, NULL, 'n'},
		{"squeeze-blank", no_argument, NULL, 's'},
		{"show-nonprinting", no_argument, NULL, 'v'},
		{"show-ends", no_argument, NULL, 'E'},
		{"show-tabs", no_argument, NULL, 'T'},
		{"show-all", no_argument, NULL, 'A'},
		{GETOPT_HELP_OPTION_DECL},
		{GETOPT_VERSION_OPTION_DECL},
		{NULL, 0, NULL, 0}
	  };
	  
	    initialize_main (&argc, &argv);
	  set_program_name (argv[0]);
	  setlocale (LC_ALL, "");
	  bindtextdomain (PACKAGE, LOCALEDIR);
	  textdomain (PACKAGE);

	  if (argc < 2) {	//如果命令行参数个数小于2，则输出使用方法
		std::cerr << "Usage: " << argv[0] << " <filename> [-n] [-s]" << std::endl;
		return 1;
	  }

	  while ((c = getopt_long (argc, argv, "benstuvAET", long_options, NULL))
		     != -1)
		{
		  switch (c)
		    {
		    case 'b':	//对输出的非空行进行编号
		      number = true;
		      number_nonblank = true;
		      break;

		    case 'e':	//在每行末尾显示 $ 字符，显示非打印字符
		      show_ends = true;
		      show_nonprinting = true;
		      break;

		    case 'n':	//对输出的所有行进行编号
		      number = true;
		      break;

		    case 's':	//将连续多个空行压缩为一行
		      squeeze_blank = true;
		      break;

		    case 't':	//将制表符显示为 ^I，显示非打印字符
		      show_tabs = true;
		      show_nonprinting = true;
		      break;

		    case 'u':	//无条件地执行该功能
		      /* We provide the -u feature unconditionally.  */
		      break;

		    case 'v':	//显示非打印字符
		      show_nonprinting = true;
		      break;

		    case 'A':	//等同于 -vET，显示所有非打印字符、行尾符和制表符
		      show_nonprinting = true;
		      show_ends = true;
		      show_tabs = true;
		      break;

		    case 'E':	//在每行末尾显示 $ 字符
		      show_ends = true;
		      break;

		    case 'T':	//将制表符显示为 ^I
		      show_tabs = true;
		      break;

		    case_GETOPT_HELP_CHAR;	//显示帮助信息

		    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);	//显示版本信息

		    default:
		      usage (EXIT_FAILURE);
		    }
		}

	if (!(number || show_ends || squeeze_blank))
	{
		file_open_mode |= std::ios_base::binary;  // 设置文件打开模式为二进制模式

		if (std::ios_base::binary && !isatty(STDOUT_FILENO)) {  // 如果不是终端输出
		    std::freopen(nullptr, "wb", stdout);  // 以二进制写模式重新打开标准输出
		}
	}

	/* 主循环 */

	infile = "-";  // 默认输入文件是标准输入
	argind = optind;

	do
	{
	  if (argind < argc)
		infile = argv[argind];  // 获取下一个输入文件名

	  if (STREQ(infile, "-"))  // 如果输入文件是标准输入
	  {
		have_read_stdin = true;  // 标记已经读取了标准输入
		input_desc = STDIN_FILENO;
		if ((file_open_mode & O_BINARY) && !isatty(STDIN_FILENO))  // 如果打开模式为二进制且不是终端输入
		  std::freopen(nullptr, "rb", stdout);  // 以二进制写模式重新打开标准输出
	  }
	  else
	  {
		input_desc = open(infile, file_open_mode);  // 打开输入文件
		if (input_desc < 0)
		{
		  std::cerr << "error" << std::endl;  // 输出错误信息
		  ok = false;
		  continue;
		}
	  }

	  if (fstat(input_desc, &stat_buf) < 0)  // 获取输入文件的状态信息
	  {
		std::cerr << "error" << std::endl;  // 输出错误信息
		ok = false;
		goto contin;
	  }

	  /* 选择使用哪个版本的'cat'。如果给定了任何面向格式的选项，则使用'cat'；
		 否则使用'simple_cat'。 */

	  if (!(number || show_ends || show_nonprinting || show_tabs || squeeze_blank))
	  {
		insize = MAX(insize, outsize);
		inbuf = malloc(insize + page_size - 1);  // 分配输入缓冲区内存

		ok &= simple_cat(ptr_align(inbuf, page_size), insize);  // 使用simple_cat处理输入
	  }
	  else
	  {
		inbuf = malloc(insize + 1 + page_size - 1);  // 分配输入缓冲区内存

		outbuf = malloc(outsize - 1 + insize * 4 + LINE_COUNTER_BUF_LEN + page_size - 1);  // 分配输出缓冲区内存

		ok &= cat(ptr_align(inbuf, page_size), insize, ptr_align(outbuf, page_size), outsize,
		          show_nonprinting, show_tabs, number, number_nonblank, show_ends, squeeze_blank);  // 使用cat处理输入

		free(outbuf);  // 释放输出缓冲区内存
	  }

	  free(inbuf);  // 释放输入缓冲区内存

	contin:
	  if (!STREQ(infile, "-") && close(input_desc) < 0)  // 如果不是标准输入并且关闭文件失败
	  {
		std::cerr << "error" << std::endl;  // 输出错误信息
		ok = false;
	  }
	}
	while (++argind < argc);  // 遍历所有输入文件

	if (have_read_stdin && close(STDIN_FILENO) < 0)  // 如果已读取标准输入并且关闭标准输入失败
	  std::cerr << "closing standard input" << std::endl;  // 输出错误信息

	exit(ok ? EXIT_SUCCESS : EXIT_FAILURE);  // 根据处理结果退出程序
}
